#include "crossbit/ExecutionEngine.h"
#include "arch/ia32/IA32ProcessorState.h"
#include "arch/ia32/IA32Interpreter.h"
#include "arch/ia32/IA32Syscall.h"
#include "arch/ia32/IA32NestedLoopDetector.h"
#include "crossbit/Exit.h"
#include "crossbit/Profiler.h"
#include "crossbit/VInst.h"
#include "crossbit/VFilter.h"
#include "crossbit/CFG.h"
#include "crossbit/VBlockAnalyzer.h"
#include "polytope/Polytope.h"
#include "vector"
#include "map"
#include "algorithm"
#include "fstream"

#include "g_crossbit/PTXExecution.h"
#include "g_crossbit/Entry.h"

using namespace std;
using namespace crossbit;
using namespace g_crossbit;
using namespace polytope;
//#define DEBUG
//#define LINK
#define GPU
//#define POLYTOPE

void ExecutionEngine::run(int time)
{
	ee_memory_image->load();

#ifdef GPU
	// added by zhangyichao, 2010-4-12
	IA32NestedLoopDetector detector( ee_interpreter );
	if( 0 == time )
	{
		detector.setTextSectionRange( ee_memory_image->text_start(), ee_memory_image->text_end() );
		detector.run();
		detector.printNestedLoops();
	}
#endif

	ee_interpreter->initStack( ee_memory_image->stack() );

	XTMemAddr entry = ee_memory_image->entry();

	TBlock* pre_tblock = NULL;
	VBlock* pre_vblock = NULL;
	GLOBAL::BUILD_SUPER_BLOCK = 0;

	VBlockAnalyzer analyzer;
#ifdef GPU
	if( 0 == time )
	{
		VBlockAnalyzer::SetStreamArea( detector.getOutmostEntry(), detector.getOutmostExit() );
#ifdef DEBUG
		VBlockAnalyzer::PrintStreamArea();
#endif
	}
#ifdef POLYTOPE
	// temporary control for polytope
	bool polytope_switch = false;
	bool vfilter_switch = false;
	bool outmost_in = false, outmost_out = false;
#endif
	/* Add by Xiaohanbo at 2009-12-23 */
	XTMemAddr gpu_entry, cpu_entry, gpu_exit;
	PTXExecution *gpu_engine = NULL;
	
	char path[100], *ptr = NULL;

	if(1 == time)		// run ia322ptx
	{
		if ( (ptr = getenv ("SCRIPT_D")) == NULL) {
			printf ("env SCRIPT_D doesn't exist.\n");
			exit (0);
		}

#ifdef DEBUG
		printf ("SCRIPT_D is: %s\n", ptr);
#endif

		strcpy (path, ptr);
		strcat (path, "/Entry/entries");

#ifdef DEBUG
		printf ("path is %s\n", path);
#endif 

		/* Load entries from file named entries. */
		Entry::loadEntries (path);
#ifdef DEBUG
		Entry::dumpEntries ();
#endif 
		gpu_entry = Entry::cur_entry->getStartSpc ();
		gpu_exit = Entry::cur_entry->getEndSpc ();
	}
#endif

	// added by zhangyichao, 2010-4-12
	ee_interpreter->setMode(NORMAL);
#ifdef POLYTOPE
	vector<XTMemAddr> loops;
#endif

	while (1) {
#ifdef GPU
		if(0 == time)
		{
			analyzer.AddVBlockCount(entry);
		}
		if(1 == time)
		{
			/*
			 * If current entry is equal to gpu_entry, then
			 * the instructions between gpu_entry and cpu_entry
			 * will be translated into PTX and executed on 
			 * GPU. The data needed by those instructions will
			 * be copied from main memory to GPU's global memory.
			 * The values of eigth general registers also be
			 * copied into GPU's global memory. The output of 
			 * those instructions also be copied from GPU's 
			 * memory to main memory.
			 */
			if (entry == gpu_entry) {
				gpu_engine = new PTXExecution (entry, (IA32ProcessorState *)ee_processor_state);
                gpu_engine->run ();
				// a bug here, so we're afraid to use this function
				//entry = Entry::cur_entry->getEndSpc ();
				entry = gpu_exit;
				continue;
			}
			/* end */
		}
#endif

		TBlock *tblock = ee_tcache -> lookupTBlock(entry);
		VBlock *vblock;
		XTMemAddr next;

		if (tblock == NULL) {
#ifdef POLYTOPE
			int loop_head = 0;
#endif
			ee_builder->getReady(entry);
			next = entry;

			while( !ee_builder->isEnd() ){
#ifdef POLYTOPE
				if(!loop_head) loop_head = detector.isNestedLoop(next);

				if( next == detector.getOutmostEntry() )
				{
					outmost_in = true;
					vfilter_switch = true;
				}
				if( next == detector.getOutmostExit() )
					outmost_out = true;
#endif
				next = ee_interpreter -> decode(next, ee_builder);
			}

#ifdef POLYTOPE
			if( outmost_in )
			{
				// TODO: save current processor state
				outmost_in = false;
			}
#endif

			vblock = ee_builder -> wrapUp(next);
			
#ifdef POLYTOPE
			if( VBlockAnalyzer::IsInStreamArea( entry ) )
#endif
				VFilter::runFilters(vblock);

#ifdef POLYTOPE
			if(loop_head > 0) {
			
				vblock->Set_is_loop_head(loop_head);
				printf("entry = 0x%x, loop_head = %d\n", entry, loop_head);

				loops.push_back(entry);
			}
#endif

			tblock = ee_tcache -> requestTBlock();
			//add by zhunan 2010-3-10
            tblock -> setSPC( entry );
            ee_translator -> encode(vblock, tblock);    
			ee_tcache -> submitTBlock(entry,tblock,vblock);
			
			tblock->VBlock_addr=vblock;
			tblock->setRunCount();

#ifdef POLYTOPE
			//zhunan add 2010-3-15
			//BUILD THE CFG

			vblock->Set_In_CFG(ee_cfg_builder->Add_CFG_Node(vblock->Get_VBlock_ID(),vblock));
#endif
			
		}//end of Tblock==NULL
#ifdef POLYTOPE
		else
		{
			// TBlock has been in the TCache
			// zhunan 3-16
			tblock->setRunCount();	
			
			//zhunan add 2010-3-15
			//updated 2010-4-26
			//here, some vblock has been invoked more than once , it means that it is in a loop path
			
			//here is the edition written in 3-16
			if(!tblock->VBlock_addr->Get_In_loop())
			{
				//we find the first node which is not in the loop path
				// so the blocks which are in the circle led by it are all in the loop path
				// and what we find is the loop_head;
			
				//don't forget to update the current last node's next_path!!!!!!!!!!!!!!!!!!!
				//
				
				tblock->VBlock_addr->Set_In_loop();
				
				if (ee_cfg_builder->CFG_Store[ee_cfg_builder->Get_last_node()].path_num==0)
				{//if the last node is new generated, we just need update hte next_block[0] directlyi

					ee_cfg_builder->CFG_Store[ee_cfg_builder->Get_last_node()].next_block[0]=tblock->VBlock_addr->Get_In_CFG();
					ee_cfg_builder->CFG_Store[ee_cfg_builder->Get_last_node()].path_num++;

				}
				else
				{//if not new generated
					if (ee_cfg_builder->CFG_Store[ee_cfg_builder->Get_last_node()].path_num==1)
					//search to see if the block has been in the next_block
					{
						if (int i = (ee_cfg_builder->next_block_exist(ee_cfg_builder->CFG_Store[ee_cfg_builder->Get_last_node()].path_num, 
								tblock->VBlock_addr->Get_In_CFG())) >=0)
						{
							if (ee_cfg_builder->CFG_Store[ee_cfg_builder->Get_last_node()].path_num==1&&i==0)
							{
								int tmp=ee_cfg_builder->CFG_Store[ee_cfg_builder->Get_last_node()].next_block[1];
									
								ee_cfg_builder->CFG_Store[ee_cfg_builder->Get_last_node()].next_block[1]
									=ee_cfg_builder->CFG_Store[ee_cfg_builder->Get_last_node()].next_block[0];
								ee_cfg_builder->CFG_Store[ee_cfg_builder->Get_last_node()].next_block[0]=tmp;;
							}

						
						}
						else
						{
							ee_cfg_builder->CFG_Store[ee_cfg_builder->Get_last_node()].next_block[1]=tblock->VBlock_addr->Get_In_CFG();
						
							ee_cfg_builder->CFG_Store[ee_cfg_builder->Get_last_node()].path_num++;
						
						}
					}	
				}
				

				//update the last_node of CFG
				ee_cfg_builder->Set_last_node(tblock->VBlock_addr->Get_In_CFG());


				if(tblock->VBlock_addr->Get_loop_head() == 1)	// outmost loop
				{
					printf("outmost loop: 0x%x\n", tblock->VBlock_addr->enterSPC());

				//search forward until the pointer back to the current vblock
				//when we meet a block which is not in the loop path we add it into the set
				//and for the head which is in the outer level of the nested loop 
				//when we meet a node which has been added into the loop path, 
				//we should just go ahead to the next and if it is a loop head, we should add the loop level of current head
				

				int current_head_pos = tblock->VBlock_addr->Get_In_CFG();

				int n = ee_cfg_builder->CFG_Store[current_head_pos].path_num;//get the lastest path

				int i = ee_cfg_builder->CFG_Store[current_head_pos].next_block[n-1];
			
				int next_pos = 0;
				vector <int> path_vec;
				int flag=0;
	//			ofstream file("log.zhunan");
				for ( ; ee_cfg_builder->CFG_Store[i].vb->Get_In_CFG() != current_head_pos; i = next_pos)
				{
					
					CFG::CFG_Node * p= &ee_cfg_builder->CFG_Store[i];
			//		ee_cfg_builder->CFG_Store[i].vb->print(file);
					
					if (!p->vb->Get_In_loop())
					{
						p->vb->Set_In_loop();
						cout<<"Add a new node:	"<<p->vb->enterSPC()<<endl;
						
					}

					if (p->vb->Get_loop_head() == 2)	// inner loops
					{//caculate the loop level
							
						cout<<"inner:	"<<p->vb->Get_In_CFG();
						printf("	enterSPC:0x%x\n",p->vb->enterSPC());

					}

					n = ee_cfg_builder->CFG_Store[i].path_num;//get the lastest path
				
					if (n==1)
					{
					  next_pos = ee_cfg_builder->CFG_Store[i].next_block[0];
					  path_vec.push_back(next_pos);
					}
					else
					{
						if (n==2)
						{
							for (int path_idx = 0; path_idx < path_vec.size(); path_idx++)
							{
								if (path_vec[path_idx]==ee_cfg_builder->CFG_Store[i].next_block[0])
								{
									flag=1;//has visit next 0
								
								}

							}

						}
						if (flag==1) 
						{
								next_pos=ee_cfg_builder->CFG_Store[i].next_block[1];
								path_vec.push_back(next_pos);
						}
						else
						{
							if (flag==0)
							{
								next_pos=ee_cfg_builder->CFG_Store[i].next_block[0];
								path_vec.push_back(next_pos);
							}
						}

					}

				
					if (n>1)
					{
							cout<<"current pos:		"<<i;
							cout<<"		NEXT0:	"<<ee_cfg_builder->CFG_Store[i].next_block[0];

							cout<<"		NEXT1:	"<<ee_cfg_builder->CFG_Store[i].next_block[1]<<endl;
						
					}
					else
					{
							
							cout<<"current pos:		"<<i;

							cout<<"NEXT0:	"<<ee_cfg_builder->CFG_Store[i].next_block[0]<<endl;

					}
				}
				
					if (tblock->VBlock_addr->Get_loop_head() == 1)
					{
						
							cout<<"outest:	"<<tblock->VBlock_addr->Get_In_CFG();
							
							printf("	enterSPC:0x%x\n",tblock->VBlock_addr->enterSPC());
					}
				}


			}//the block is not in the loop path
			else
			{//for the blocks which have been in the path, just go ahead
					
				ee_cfg_builder->Set_last_node(tblock->VBlock_addr->Get_In_CFG());
			}

			// added by zhangyichao, 2010-5-5
			if( outmost_out )
			{
				printf("polytope_switch on at 0x%x\n", entry);
				polytope_switch = true;
				vfilter_switch = false;
				outmost_out = false;
			}


		}//end of TBlock has been in the Tcache
		//ee_cfg_builder->Print_CFG();i
		
		
		
		//zhunan 2010-3-23
		//here, we began to add the operations into the polytope space

#endif

		XTMemAddr enter = tblock->enterAddr();

#ifdef LINK

		if( pre_tblock != NULL )
		{
		    VInst *inst = *pre_vblock->rbegin();
		    if( inst->getOpcode() == OP_BRANCH && inst->getOperand(4).reg == 0)
		    {
		        ee_trace_manager -> directLinkTo( pre_tblock , GLOBAL::last_exit , tblock );
		    }
		    else if(inst->getOpcode() == OP_JMP && inst->getOperand(1).reg != 0)
		    {
		        ee_trace_manager -> indirectLinkTo( pre_tblock , GLOBAL::last_exit , tblock );
		    }
		}
#endif

    CONTEXT_SAVE;

	 		((void (*)()) enter) ();

    CONTEXT_RESTORE;
#ifdef POLYTOPE
		if( polytope_switch )
		{
			if (tblock->VBlock_addr->Get_loop_head()==1)
			{
				//build the polytope with 1-dimension
				ee_polytope_builder->Build_Polytope(
					tblock->VBlock_addr,
					ee_cfg_builder,
					1,
					128);

			}
			if (tblock->VBlock_addr->Get_loop_head()==1)
			{
				polytope_switch = false;
			}
		}
#endif
		

#ifdef POLYTOPE
		if( vfilter_switch )
#endif
			VFilter::finalizeFilters(vblock);

		Exit exit = GLOBAL::tblock_stub->getExit(GLOBAL::last_exit);
		if (exit.why == ExitReason::SYSCALLEXIT) {
#if 0
			if( ((IA32Syscall*)ee_syscall_handler) -> syscode() == 252 )
			{
				FILE *ma_file = fopen("ma_ld_info", "w");
				std::map< XTMemAddr, vector< XTMemAddr > >::iterator mit = GLOBAL::ma_ld_info.begin();
				while(mit != GLOBAL::ma_ld_info.end())
				{
					fprintf( ma_file, "VBlock 0x%x:\n", mit->first );
					/*
					std::vector<XTMemAddr>::iterator vit = mit->second.begin();
					while(vit != mit->second.end())
					{
						fprintf( ma_file, " 0x%x", *vit );
						vit++;
					}
					fprintf( ma_file, "\n" );
					*/
					mit++;
				}
				fclose( ma_file );
				ma_file = fopen( "ma_st_info", "w" );
				mit = GLOBAL::ma_st_info.begin();
				while(mit != GLOBAL::ma_st_info.end())
				{
					fprintf( ma_file, "VBlock 0x%x:\n", mit->first );
					/*
					std::vector<XTMemAddr>::iterator vit = mit->second.begin();
					while(vit != mit->second.end())
					{
						fprintf( ma_file, " 0x%x", *vit );
						vit++;
					}
					fprintf( ma_file, "\n" );
					*/
					mit++;
				}
				fclose( ma_file );
			}
#endif
#ifdef GPU
			if(0 == time)
			{
				if( ((IA32Syscall*)ee_syscall_handler) -> syscode() == 252 )
				{
					analyzer.FindSpeedUpArea(ee_builder, ee_interpreter);
					analyzer.AnalyzeDataFlows();
					analyzer.DumpEntries();
					analyzer.DumpRecords();
					analyzer.DumpVBlocksText();
					//analyzer.DumpVBlockByte();
				}
			}
#endif
			(*ee_syscall_handler)();
		}

#ifdef GPU
		if(0 == time)
		{
			analyzer.FindLoop(entry, exit.next);
		}
#endif

		entry = exit.next;

		pre_tblock = GLOBAL::tblock_stub;
		pre_vblock = GLOBAL::vblock_stub;
	}//end of while(1)
	//ee_cfg_builder->Print_CFG();
}//end of function

